<?php
namespace core;
/**
 * 
 * @author jlb <497012571@qq.com>
 */
class App
{
    static $instance = FALSE;

    private $crontabConfig = FALSE;


    private function __construct()
    {

        self::$instance =& $this;

        $this->crontabConfig = config('crontab');

        if ( !is_dir(TMP_CRONTAB) )
        {
            mkdirs(TMP_CRONTAB);
        }

        //删除残留的临时目录
        @exec("rm -rf " . TMP_CRONTAB . '*');

        //启动work
        $this->crontabScriptStart();
    }

    /**
     * 开启进程,并发处理任务
     * @return [type] [description]
     */
    public function crontabScriptStart()
    {
        //常驻进程开启
        while (true) 
        {
            //用于获取未运行或者已过期的计划任务
            $scriptList = $this->getScriptList();
            //表明没有要运行的任务
            if ( empty($scriptList) ) 
            {
                continue;
            }

            foreach ($scriptList as $script => $config) 
            {
                $forkNum = isset($this->crontabConfig[$script]['fork']) ? $this->crontabConfig[$script]['fork'] : 1;
                $this->runFork($script, $forkNum);
            }

            //回收子进程
            while (pcntl_waitpid(0, $status) != -1) { 
                $status = pcntl_wexitstatus($status); 
            }

        }
    }

    /**
     * 开启fork,并执行
     * @param  [type] $scriptName [fork名字]
     * @param  [type] $forkNum    [创建的数量]
     * @return [type]             [description]
     */
    public function runFork($scriptName, $forkNum=1)
    {
        if ( !$forkNum )
        {
            return '';
        }

        //先尝试实例化脚本任务,并判断是否是有效的脚本;
        $objName = "script\\" . (str_replace('/', '\\', $scriptName));
        $scriptObj = new $objName;
        if ( !is_object($scriptObj) )
        {
            return ( $scriptName . ',创建fork失败,原因:脚本实例化失败!' . "\n" );
        }
        if ( !method_exists($scriptObj, 'run') )
        {
            return ( $scriptName . ',创建fork失败,原因:无入口方法:run()!' . "\n");
        }

        //创建临时文件,标识该脚本已经执行.
        mkdirs(dirname(TMP_CRONTAB . $scriptName . '.timer'));
        touch(TMP_CRONTAB . $scriptName . '.timer');

        /**
         * 开始创建fork
         */
        for ($i=1; $i <= $forkNum; $i++) 
        { 

            $pid = pcntl_fork();
            if ( !$pid )
            {
                $scriptObj->run();
                exit(0);
            }
            
        }

    }

    /**
     * 获取可以再次运行的脚本
     * @return $scriptList
     */
    public function getScriptList()
    {
        $scriptList = [];
        foreach ($this->crontabConfig as $script => $config) {

            //第一次运行
            if ( !is_file(TMP_CRONTAB . $script . '.timer') ) 
            {
                $scriptList[$script] = $config;
                continue;
            }

            $fcreatTime = filectime(TMP_CRONTAB . $script . '.timer');
            //判断任务是否过期
            if ( (time() - $fcreatTime) >= $config['timer'] ) 
            {
                @unlink(TMP_CRONTAB . $script . '.timer');
                $scriptList[$script] = $config;
            }

        }

        return $scriptList;
    }


    static public function start()
    {
        if (!self::$instance) 
        {
            self::$instance = new self();
        }
        return self::$instance;
    }

}


